package com.atguigu.tingshu.account.service.impl;

import com.atguigu.tingshu.account.mapper.UserAccountDetailMapper;
import com.atguigu.tingshu.account.mapper.UserAccountMapper;
import com.atguigu.tingshu.account.service.UserAccountService;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.constant.SystemConstant;
import com.atguigu.tingshu.common.constant.TingShuMqConstants;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.result.ResultCodeEnum;
import com.atguigu.tingshu.common.service.RabbitmqService;
import com.atguigu.tingshu.common.util.AuthContextHolder;
import com.atguigu.tingshu.model.account.UserAccount;
import com.atguigu.tingshu.model.account.UserAccountDetail;
import com.atguigu.tingshu.model.order.OrderInfo;
import com.atguigu.tingshu.order.client.OrderInfoFeignClient;
import com.atguigu.tingshu.vo.account.AccountLockResultVo;
import com.atguigu.tingshu.vo.account.AccountLockVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.security.core.parameters.P;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.concurrent.TimeUnit;

@Slf4j
@Service
@SuppressWarnings({"unchecked", "rawtypes"})
public class UserAccountServiceImpl extends ServiceImpl<UserAccountMapper, UserAccount> implements UserAccountService {
    @Resource
    private UserAccountMapper userAccountMapper;
    @Resource
    private RedisTemplate redisTemplate;
    @Resource
    private UserAccountDetailMapper userAccountDetailMapper;
    @Resource
    private RabbitmqService rabbitmqService;
    @Resource
    private OrderInfoFeignClient orderInfoFeignClient;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void initUserAccount(Long userId) {
        UserAccount userAccount = new UserAccount();
        userAccount.setUserId(userId);
        save(userAccount);
    }

    @Override
    public BigDecimal getAvailableAmount() {
        Long userId = AuthContextHolder.getUserId();
        if (userId == null) return null;
        UserAccount userAccount = this.getOne(new LambdaQueryWrapper<UserAccount>().eq(UserAccount::getUserId, userId));
        return userAccount.getAvailableAmount();
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public AccountLockResultVo checkAndLockAccount(AccountLockVo accountLockVo) {
        String orderNo = accountLockVo.getOrderNo();
        Long userId = accountLockVo.getUserId();
        BigDecimal amount = accountLockVo.getAmount();
        String idemKey = RedisConstant.ACCOUNT_LOCK_IDEM_PREFIX + orderNo;
        String infoKey = RedisConstant.ACCOUNT_LOCK_INFO_PREFIX + orderNo;
        //幂等性校验
        Boolean flag = this.redisTemplate.opsForValue().setIfAbsent(idemKey, "", RedisConstant.IDEM_TIMEOUT, TimeUnit.SECONDS);
        if (Boolean.FALSE.equals(flag)) {//校验失败，则重复锁定
            AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(infoKey);
            if (accountLockResultVo == null) throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_REPEAT);
            return accountLockResultVo;
        }
        //校验成功
        try {
            //校验余额
            UserAccount userAccount = this.userAccountMapper.checkAccount(userId, amount);
            if (userAccount == null) throw new GuiguException(ResultCodeEnum.ACCOUNT_LESS);
            //锁定余额
            int row = this.userAccountMapper.lockAccount(userId, amount);
            if (row == 0) {
                throw new GuiguException(ResultCodeEnum.ACCOUNT_LOCK_ERROR);
            }
            //余额锁定成功，记录账户变化信息
            this.userAccountDetailMapper.insert(new UserAccountDetail().setUserId(userId).setTitle("锁定余额")
                    .setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_LOCK)
                    .setAmount(amount).setOrderNo(orderNo));
            //响应数据生成
            AccountLockResultVo accountLockResultVo = new AccountLockResultVo().setUserId(userId).setAmount(amount);
            this.redisTemplate.opsForValue().set(infoKey, accountLockResultVo,RedisConstant.CACHE_TIMEOUT,TimeUnit.SECONDS);
            //发送延时消息，解锁余额
            this.rabbitmqService.sendMqMsg(TingShuMqConstants.ORDER_EXCHANGE,TingShuMqConstants.ACCOUNT_TTL_RK,orderNo);
            return accountLockResultVo;
        } catch (Exception e) {
            if (e instanceof GuiguException) throw e;
            e.printStackTrace();
            this.redisTemplate.delete(idemKey);
        }
        throw new GuiguException(ResultCodeEnum.ILLEGAL_REQUEST);
    }

    @Override
    public void deductionAmount(String orderNo) {
        //获取redis缓存的锁定信息
        String infoKey = RedisConstant.ACCOUNT_LOCK_INFO_PREFIX + orderNo;
        AccountLockResultVo accountLockResultVo = (AccountLockResultVo) this.redisTemplate.opsForValue().get(infoKey);
        if (accountLockResultVo == null) return;
        BigDecimal amount = accountLockResultVo.getAmount();
        Long userId = accountLockResultVo.getUserId();
        //扣减金额
        int row = this.baseMapper.deduct(userId,amount);
        if (row == 0) return;
        //保存账户变化信息
        this.userAccountDetailMapper.insert(new UserAccountDetail().setUserId(userId)
                .setTitle("余额扣减").setTradeType(SystemConstant.ACCOUNT_TRADE_TYPE_MINUS)
                .setAmount(amount).setOrderNo(orderNo));
        //删除锁定信息的缓存
        this.redisTemplate.delete(infoKey);
        //发送消息，修改订单的支付状态
        this.rabbitmqService.sendMqMsg(TingShuMqConstants.ORDER_EXCHANGE, TingShuMqConstants.ORDER_STATUS_RK, orderNo);
    }

    @Override
    public int unlockAccount(Long userId, BigDecimal amount) {
        return this.baseMapper.unlockAccount(userId,amount);
    }

    @Override
    public void refund(String orderNo) {
        Result<OrderInfo> orderInfoResult = this.orderInfoFeignClient.getOrderInfo(orderNo);
        Assert.notNull(orderInfoResult, "申请退款时，获取订单信息失败！");
        OrderInfo orderInfo = orderInfoResult.getData();
        if(orderInfo == null) return;
        BigDecimal orderAmount = orderInfo.getOrderAmount();
        this.baseMapper.refund(orderInfo.getUserId(), orderAmount);
    }
}
